<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<style>
    #map {
        width: 400px;
        height: 400px;
        position: relative;
        background-color: black;
    }
</style>
<body>
<div id="grade">0</div>
<div id="map">

</div>
<script>
    class Map {
        constructor(el, rect = 10) {
            this.el = el;
            this.rect = rect;
            this.data = [
                // {
                //     x:0,
                //     y:0,
                //     color:"red"
                // }
            ]
            this.rows = Math.ceil(Map.getStyle(el, "height") / rect);
            this.cells = Math.ceil(Map.getStyle(el, "width") / rect);

            Map.setStyle(el, "height", this.rows * rect);
            Map.setStyle(el, "width", this.cells * rect);
        }

        static getStyle(el, attr) {
            return parseFloat(getComputedStyle(el)[attr])
        }

        static setStyle(el, attr, val) {
            el.style[attr] = val + "px";
        }

        setData(newData) {
            this.data = this.data.concat(newData);
        }

        getData() {

        }

        clearData() {
            this.data.length = 0;
        }

        include({x, y}) {
            return this.data.some(item => (item.x == x && item.y == y));
        }

        render() {
            this.el.innerHTML = this.data.map(item => {
                return `<span style="position:absolute;background: ${item.color};width: ${this.rect}px;height: ${this.rect}px;
                    left: ${item.x * this.rect}px;top: ${item.y * this.rect}px;"></span>`
            }).join("")
        }
    }

    class Food {
        constructor(map, colors = ["red", "blue", "yellow", "pink"]) {
            this.map = map;
            this.data = null;
            this.colors = colors;
            this.create();
        }

        create() {
            let x = Math.floor(Math.random() * this.map.cells);
            let y = Math.floor(Math.random() * this.map.rows);
            let color = this.colors[parseInt(Math.random() * this.colors.length)];
            this.data = {x, y, color}
            if (this.map.include(this.data)) {
                this.create();
            }
            this.map.setData(this.data)
        }
    }

    class Snake {
        constructor(map, food) {
            this.data = [
                {x: 6, y: 4, color: "green"},
                {x: 5, y: 4, color: "#fff"},
                {x: 4, y: 4, color: "#fff"},
                {x: 3, y: 4, color: "#fff"},
                {x: 2, y: 4, color: "#fff"}
            ]
            this.map = map;
            this.food = food;
            this.direction = "right"
            this.map.setData(this.data);
        }

        move() {
            let i = this.data.length - 1;
            this.lastData = {
                x: this.data[i].x,
                y: this.data[i].y,
                color: this.data[i].color
            }
            for (i; i > 0; i--) {
                this.data[i].x = this.data[i - 1].x
                this.data[i].y = this.data[i - 1].y;

            }
            switch (this.direction) {
                case "left":
                    this.data[0].x--;
                    break;
                case "right":
                    this.data[0].x++;
                    break;
                case "top":
                    this.data[0].y--;
                    break;
                case "bottom":
                    this.data[0].y++;
                    break;
            }
        }

        changeDir(dir) {
            // 如果蛇本身现在左右移动，我们只能修改让蛇上下移动
            if (this.direction === "left" || this.direction === "right") {
                if (dir === "right" || dir === "left") {
                    return false;// 不能修改方向
                }
                this.direction = dir;
            } else {
                if (dir === "top" || dir === "bottom") {
                    return false;// 不能修改方向
                }
                this.direction = dir;
                return true;
            }
            // 如果蛇正在上下移动，我们只能让蛇左右移动你
        }

        eatFood() {
            this.data.push(this.lastData)
        }

    }

    class Game {
        constructor(el, rect, toControl = null, toGrade = null) {
            this.map = new Map(el, rect)
            this.food = new Food(this.map);
            this.snake = new Snake(this.map)
            this.map.render();
            this.timer = 0;
            this.grade = 0;
            this.interval = 200;
            this.toControl = toControl;
            this.toGrade = toGrade;
            this.keyDown = this.keyDown.bind(this)
            this.control();
        }

        // 开始游戏
        start() {
            this.move();
        }

        // 暂停游戏
        stop() {
            clearInterval(this.timer);
        }

        // 控制移动
        move() {
            this.stop();
            this.timer = setInterval(() => {
                this.snake.move();
                this.map.clearData();

                if (this.isEat()) {
                    this.grade++;
                    console.log("吃到食物")
                    this.snake.eatFood();
                    this.food.create();
                    this.changeGrade(this.grade);
                    this.interval *= 0.9
                    this.stop();
                    this.start();
                    if (this.grade >= 20) {
                        this.over(1)
                    }
                }
                if (this.isOver()) {
                    this.over(0);
                    return;
                }
                this.map.setData(this.snake.data)
                this.map.setData(this.food.data)
                this.map.render()
            }, this.interval)
        }

        // 是否吃到食物
        isEat() {
            return this.snake.data[0].x === this.food.data.x && this.snake.data[0].y === this.food.data.y;
        }

        // 判断是否结束游戏
        isOver() {
            // 判断蛇出了地图
            console.log(this.map.cells,this.map.rows)
            if (this.snake.data[0].x < 0 || this.snake.data[0].x >= this.map.cells
                || this.snake.data[0].y < 0 || this.snake.data[0].y >= this.map.rows) {
                return true;
            }
            // 判断蛇撞到了自己
            for (let i = 1; i < this.snake.data.length; i++) {
                if (this.snake.data[0].x === this.snake.data[i].x && this.snake.data[0].y === this.snake.data[i].y) {
                    return true;
                }
            }

            return false;
        }

        // 游戏结束
        /**
         * overState 0:中间停止 1：胜利
         * @param overState
         */
        over(overState = 0) {
            if (overState) {
                this.toWin && this.toWin()
            } else {
                this.toOver && this.toOver();
            }
            this.stop()
        }

        // 分数改变
        changeGrade(grade) {
            this.toGrade && this.toGrade(grade)
        }

        keyDown({keyCode}) {
            console.log(keyCode)
            let isDir;
            switch (keyCode) {
                case 37:
                    isDir = this.snake.changeDir("left")
                    break;
                case 38:
                    isDir = this.snake.changeDir("top")
                    break;
                case 39:
                    isDir = this.snake.changeDir("right")
                    break;
                case 40:
                    isDir = this.snake.changeDir("bottom")
                    break;
            }
            console.log(isDir)
        }

        // 控制器
        control() {
            if (this.toControl) {
                this.toControl()
                return;
            }
            window.addEventListener("keydown", this.keyDown)
        }

        addControl(fn) {
            fn.call(this)
            window.removeEventListener("keydown", this.keyDown)
        }
    }

    {
        let map = document.querySelector("#map");
        let gradeEl = document.querySelector("#grade")
        let game = new Game(map, 10)

        game.toGrade = function (grade) {
            gradeEl.innerHTML = grade;
            console.log(grade)
        }
        // game.addControl(function(){
        //     window.addEventListener("keydown",({keyCode})=>{
        //         console.log(keyCode);
        //         switch (keyCode){
        //             case 65:
        //                 this.snake.changeDir("left")
        //                 break;
        //             case 87:this.snake.changeDir("top")
        //                 break;
        //             case 68:
        //                 this.snake.changeDir("right")
        //                 break;
        //             case 83:
        //                 this.snake.changeDir("bottom")
        //                 break;
        //         }
        //     })
        // })
        document.onclick = function () {
            game.start()
        }

        game.toOver = function () {
            alert("游戏结束")
        }
        game.toWin = function () {
            alert("太棒了 ")
        };
        // let gameMap = new Map(map, 10)
        // let gameFood = new Food(gameMap);
        // let gameSnake = new Snake(gameMap, gameFood)
        // gameMap.render();

        // setInterval(()=>{
        //     gameSnake.move();
        //     console.log(gameSnake.data);
        //     gameMap.clearData()
        //     gameMap.setData(gameSnake.data);
        //     gameMap.render()
        // },200)
        // setTimeout(()=>{
        //     gameSnake.changeDir("bottom")
        //     gameSnake.eatFood();
        // },2000)
        // setTimeout(()=>{
        //     gameSnake.changeDir("left")
        //     gameSnake.eatFood();
        // },4000)
        // setTimeout(()=>{
        //     gameSnake.changeDir("top")
        //     gameSnake.eatFood();
        // },6000)
        // setInterval(()=>{
        //     gameFood.create();
        //     gameMap.setData(gameFood.data);
        //     gameMap.render();
        // },200)
        // gameFood.create();

        // gameMap.setData([
        //     {
        //         x:0,
        //         y:10,
        //         color:"green"
        //     },
        //     {
        //         x:1,
        //         y:10,
        //         color:"#fff"
        //     },
        //     {
        //         x:2,
        //         y:10,
        //         color:"#fff"
        //     },
        //     {
        //         x:3,
        //         y:10,
        //         color:"#fff"
        //     },
        // ])
        // gameMap.setData([
        //     {
        //         x:10,
        //         y:10,
        //         color:"red"
        //     }
        // ])
        // console.log(gameMap)
        // gameMap.render();
    }
</script>
</body>

</html>
